Inside Macintosh: Sound

| Previous | Chapter contents | Chapter top | Section top | Next |

Finding a Chunk in a Sound File

Sound files are not as tightly structured as sound resources. As explained in "Sound Files" , the chunks in a sound file can appear in any order, except that the Form Chunk is always first. Most information about a sampled sound stored in a sound file is contained in the Common Chunk. Thus, to be able to access this information, you must be able to find a particular kind of chunk in a sound file. Listing 1-41 defines a procedure that you can use to find the location of the first chunk of a specified type beginning at the chunk you specify.

The techniques illustrated in this section are provided primarily to help you understand the structure of sound files. Most sound-producing applications don't need to parse sound files.

Listing 41 Finding a chunk in a sound file

FUNCTION MyFindChunk (myFile: Integer;                      {file reference number}
                            myChunkSought: ID;              {ID of chunk sought}
                            startPos: LongInt;              {file position to start at}
                            VAR chunkFPos: LongInt)         {file position of found chunk}
                            : OSErr;
VAR
    myLength:               LongInt;                        {number of bytes to read}
    myChunkHeader:          ChunkHeader;                    {characteristics of chunk}
    found:                  Boolean;                        {flag variable}
    myErr:                  OSErr;                          {error from File Manager calls}
BEGIN
    found := FALSE;                                         {initialize flag variable}
                                                            {set file mark at start}
    myErr := SetFPos(myFile, fsFromStart, startPos);

    {Search file's chunks for desired chunk ID.}
    WHILE (NOT found) AND (myErr = noErr) DO
    BEGIN                                                   {check current chunk}
        myLength := SizeOf(myChunkHeader);
        {Load chunk header.}
        myErr := FSRead(myFile, myLength, @myChunkHeader);
        IF myErr = noErr THEN                               {chunk header loaded okay}
            IF myChunkHeader.ckID = myChunkSought THEN
            BEGIN
                found := TRUE;                              {chunk has been found}
                                                            {find position in file}
                myErr := GetFPos(myFile, chunkFPos);
                                                            {compute chunk's start position}
                chunkFPos := chunkFPos - SizeOf(myChunkHeader);
            END
            ELSE
            BEGIN                                           {move to next chunk}
                IF myChunkHeader.ckID = ID(FormID) THEN
                    {Adjust Form Chunk's size to size of formType field.}
                    myChunkHeader.ckSize := SizeOf(ID);
                IF myChunkHeader.ckSize MOD 2 = 1 THEN
                    {Compensate for pad byte.}
                    myChunkHeader.ckSize := myChunkHeader.ckSize + 1;
                myErr := SetFPos(myFile, fsFromMark, myChunkHeader.ckSize);
            END;
    END; {WHILE}
    MyFindChunk := myErr;
END;

The MyFindChunk function defined in Listing 1-41 accepts four parameters. The myFile parameter is the file reference number of an open sound file. (For information on file reference numbers, see Inside Macintosh: Files .) In the myChunkSought parameter, you pass the ID of the type of chunk you wish to find. For example, you might pass ID(FormID) to find the Form Chunk. The third parameter, startPos , is the file position at which MyFindChunk should start searching for a chunk. This file position must be the beginning of a chunk. To start at the beginning of a file, specify 0. Finally, if the MyFindChunk function is successful, it returns in the chunkFPos parameter the file position of the first chunk of the specified type that it found. If the function is unsuccessful, it returns the appropriate File Manager result code (such as an end-of-file error) and the chunkFPos parameter is undefined.

The MyFindChunk function works by looking at each chunk of the sound file, beginning at the file position startPos and checking to see if the chunk is of the type sought. If a chunk matches, the MyFindChunk function returns the file position of the start of the chunk; otherwise, the function moves onto the next chunk. For each chunk, the MyFindChunk function reads in the chunk header, checks for a match, and then moves to the next chunk.

The MyFindChunk function moves from one chunk to the next by identifying the size of the current chunk, not including the chunk header, from the ckSize field of the chunk header. Whenever you parse sound files, you should always use the ckSize field of the chunk header to determine the size of a chunk if the size of the chunk could vary in size. The MyFindChunk function adjusts the value in the ckSize field before advancing to the next chunk in two cases. First, the ckSize field for the Form Chunk reflects the size of the entire sound file, so this function changes it to the size of the formType field so that the function does not skip the file's local chunks. Second, if the ckSize field is odd, 1 byte is added because the number of bytes in a chunk is always even.

After using the MyFindChunk function defined in Listing 1-41 , you might still need to read the data contained in a chunk into memory. For example, you might read in the Form and Common Chunks to obtain information about a sound file. Listing 1-42 uses the MyFindChunk function to find a chunk in a sound file, allocates an appropriately sized block of memory for that chunk, and reads the chunk into that block.

Listing 42 Loading a chunk from a sound file

FUNCTION MyGetChunkData (myFile: Integer;                       {file reference number}
                                    myChunkSought: ID;          {ID of chunk sought}
                                    startPos: LongInt):         {file position to start at}
                                    Ptr;                        {pointer to data or NIL}
VAR
    myFPos:                 LongInt;                            {position in file}
    myLength:               LongInt;                            {number of bytes to read}
    myChunkHeader:          ChunkHeader;                        {characteristics of a chunk}
    myChunkData:            Ptr;                                {pointer to chunk data}
    myErr:                  OSErr;
BEGIN
    myChunkData := NIL;                                         {initialize variable}
    myErr := MyFindChunk(myFile, myChunkSought, startPos, myFPos);
    IF myErr = noErr THEN
                                                                {move to start of chunk}
        myErr := SetFPos(myFile, fsFromStart, myFPos);
    IF myErr = noErr THEN
    BEGIN                                           {determine how much data to copy}
        myLength := SizeOf(ChunkHeader);
        myErr := FSRead(myFile, myLength, @myChunkHeader);
        IF myChunkHeader.ckID = ID(FormID) THEN
            myChunkHeader.ckSize := SizeOf(ID);                 {don't return local chunks}
        myLength := myChunkHeader.ckSize + SizeOf(ChunkHeader);
        IF myErr = noErr THEN
                                                                {return to chunk's start}
            myErr := SetFPos(myFile, fsFromStart, myFPos);
    END;
    IF myErr = noErr THEN
    BEGIN                                               {read chunk data into RAM}
        myChunkData := NewPtr(myLength);
        IF myChunkData <> NIL THEN
            myErr := FSRead(myFile, myLength, myChunkData);
    END;
    IF myErr <> noErr THEN
        IF myChunkData <> NIL THEN
            DisposePtr(myChunkData);
    MyGetChunkData := myChunkData;
END;

The MyGetChunkData function defined in Listing 1-42 attempts to find a chunk in a file. If it finds the chunk, it reads the chunk header to determine the chunk's size, and if the chunk is the Form Chunk, adjusts the chunk size so that the sound file's local chunks are not included in the chunk size. Then the function attempts to allocate memory for the chunk and read the chunk into the memory. If a problem occurs at any time, the function simply returns NIL .

The format of a sound file might not be the same as its operating-system type. In particular, a file might have an operating-system type 'AIFC' but be formatted as an AIFF file because the sampled-sound data contained in the file is noncompressed.


© 1998 Apple Computer, Inc.

| Previous | Chapter contents | Chapter top | Section top | Next |